/*
 * Algo.java
 *
 * Created on December 14, 2000, 8:40 PM
 */

package com.gemalto.Kryptik.Algo;

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import com.gemalto.Kryptik.Algo.*;
import com.gemalto.Kryptik.Util.*;
import com.gemalto.Kryptik.Gui.*;

/**
 * 
 * @author  STURC
 * @version 
 */
public class GenericAlgo extends KeyedAlgo implements ISymetricCipher, ISignature {

	public static final int CBC = 1;
	public static final int ECB = 2;


	private String cipherName;
	private String provider;
	private String mode;
	private String padding;
	private int blockLength;

	private IvParameterSpec iv;


	public String getCipherName(){
		return cipherName;
	}


	  /** Creates new Algo */
	  public GenericAlgo(String algoName, String shortName, String cipherName, String provider, int keyLength, int blockLength) {
		this.algoName  = algoName;
		this.shortName = shortName;
		this.cipherName= cipherName;
		this.keyLength = keyLength;
		this.blockLength = blockLength;
		this.provider = provider;
		this.mode="CBC";
		this.padding="FF";
		
		
		byte[] ivarray = new byte[blockLength];
		for (int i=0;i<blockLength;i++ )
		{ ivarray[i]=(byte)0;
		}
		iv = new IvParameterSpec(ivarray);
		//panel = new GenericAlgoPanel(this, keyLength);		
	    }


	public void setMode(String mode){
		this.mode=mode;
		panel.update();
	}

	public String getMode(){
		return this.mode;
	}

	public void setPadding(String padding){
		this.padding=padding;
		panel.update();
	}

	public String getPadding(){
		return this.padding;
	}


	
	protected boolean hasValidKey(){
		if ( getKey() != null )//&&  getKey() instanceof RawSecretKey ) 	    
				return true;
		else
				return false;
	}
/*
	public void setKey(String key){
		this.key=new RawSecretKey(getCipherName(),Hex.fromString(key));
	}


	public String getKey(){
		return Hex.dumString(((RawSecretKey)key).getEncoded());
	}
*/

	/** 
	 * encrypts given text using current algo.
	 * 
	 * @param plainText 
	 * @return encrypted text
	 * @exception AlgoException 
	 */
	public String doEncryption(String plainText) throws AlgoException {
	    String result;
	    String t = Util.omitSpaces(plainText);    
	    String padding=getPadding();

	    if (t.length()%2!=0){
		throw new AlgoException("Incorrect length of plain text!");
	    } else if (!hasValidKey()){
		throw new AlgoException("Key for this algo is not valid or missing!");   
	    }
	    
	    try {
	    StringBuffer sb = new StringBuffer(t);

	/* Padding used in Simera cards:
	   append 8 - (length mod 8) times byte 0xFF
	*/
	    if ( padding.equals("FF") || padding.equals("00")){
	    	if ((t.length()/2)%blockLength!=0){
			int remaining_bytes=blockLength-(t.length()/2)%blockLength;
			for (int i=0;i<remaining_bytes;i++){
				sb.append(padding);
			}
		}
//		System.out.println("PAD>> "+padding);
//		System.out.println("TEXT>> "+sb);
		padding="NoPadding";
	    }


	    if ( padding.equals("80")){
		if ((t.length()/2)%blockLength==0){
			sb.append("80000000000000");
	    	} else {
			int remaining_bytes=blockLength-(t.length()/2)%blockLength;
			sb.append("80");
			remaining_bytes--;
			for (int i=0;i<remaining_bytes;i++){
				sb.append("00");
			}
		}
		padding="NoPadding";
	    }

	
	
	    Cipher algoR  = Cipher.getInstance(cipherName+"/"+getMode()+"/"+padding);
	    SecretKeySpec keySpec= new SecretKeySpec(Hex.fromString(getKey()), cipherName);
	    
		if (getMode().equals("ECB"))
		{algoR.init(Cipher.ENCRYPT_MODE,keySpec);
		}
		else
		{ algoR.init(Cipher.ENCRYPT_MODE,keySpec,iv);
	    }

	    result=Hex.toString(algoR.doFinal(Hex.fromString(sb.toString())));
    
	    return result; 
	    
	    } catch (Exception e){ 
	    	throw new AlgoException(e.getMessage());
	    }
	  }




	 public String doDecryption(String encryptedText) throws AlgoException {
	    String t = Util.omitSpaces(encryptedText);    
	    String padding=getPadding();

	    if (padding.equals("FF") || padding.equals("00") || padding.equals("80")){
	    	padding="NoPadding";
	    }

	    if (t.length()%2!=0){
		throw new AlgoException("Incorrect length of plain text!");
	    } else if (!hasValidKey()){
		throw new AlgoException("Key for this algo is not valid or missing!");   
	    }

	    try {	    
	    Cipher algoR  = Cipher.getInstance(cipherName+"/"+getMode()+"/"+padding);
	    SecretKeySpec keySpec= new SecretKeySpec(Hex.fromString(getKey()), cipherName);
	    
	    if (getMode().equals("ECB"))
		{algoR.init(Cipher.DECRYPT_MODE,keySpec);
		}
		else
		{ algoR.init(Cipher.DECRYPT_MODE,keySpec,iv);
	    }


	    StringBuffer result = new StringBuffer(Hex.toString(algoR.doFinal(Hex.fromString(t))));
	
	    if (getPadding().equals("80")){
              int index = result.length()-1;
	      while( result.charAt(index)!='8' ) result.deleteCharAt(index--);	
	      result.deleteCharAt(index--);
	    }
	    return result.toString();	
	
	    } catch (Exception e){ 
	    	throw new AlgoException(e.getMessage());
	    }
	  }




	public String doSignature(String plainText) throws AlgoException {

	    String t = Util.omitSpaces(plainText);        
	    String padding=getPadding();

	    if (t.length()%2!=0){
		throw new AlgoException("Incorrect length of plain text!");
	    } else if (!hasValidKey()){
		throw new AlgoException("Key for this algo is not valid or missing!");   
	    }
	    
	    try {
	    StringBuffer sb = new StringBuffer(t);

	/* Padding used in Simera cards:
	   append 8 - (length mod 8) times byte 0xFF
	*/
	    if ( getPadding().equals("FF") || padding.equals("00")){
	    	if ((t.length()/2)%blockLength!=0){
			for (int i=0;i<blockLength-(t.length()/2)%blockLength;i++){
				sb.append(padding);
			}
		}
		padding="NoPadding";
	    }
	   
	    if ( padding.equals("80")){
		if ((t.length()/2)%blockLength==0){
			sb.append("80000000000000");
	    	} else {
			int remaining_bytes=blockLength-(t.length()/2)%blockLength;
			sb.append("80");
			remaining_bytes--;
			for (int i=0;i<remaining_bytes;i++){
				sb.append("00");
			}
		}
		padding="NoPadding";
	    }


	   
	    Cipher algoR  = Cipher.getInstance(cipherName+"/"+getMode()+"/"+padding);
	    SecretKeySpec keySpec= new SecretKeySpec(Hex.fromString(getKey()), cipherName);
	    
	    if (getMode().equals("ECB"))
		{algoR.init(Cipher.ENCRYPT_MODE,keySpec);
		}
		else
		{ algoR.init(Cipher.ENCRYPT_MODE,keySpec,iv);
	    }
	    String sign = Hex.toString(algoR.doFinal(Hex.fromString(sb.toString())));

	    return sign.substring(sign.length()-(2*blockLength));
	    } catch (Exception e){ 
	    	throw new AlgoException(e.getMessage());
	    }
	  }


}
